import datetime
import json
from enum import Enum

from sqlalchemy import UniqueConstraint

from app.models import BaseModel
from app.models.questionModel import CtfQuestion
from app.models.rangeModel import RangeLinkModule
from app.models.userModel import User
from tools import db
from utils.enums import IntegerChoices


class DateEncoder(json.JSONEncoder):
    def default(self, obj):
        if isinstance(obj, datetime.datetime):
            return obj.strftime("%Y-%m-%d %H:%M:%S")
        else:
            return json.JSONEncoder.default(self, obj)


class Ctf(db.Model, BaseModel):
    __tablename__ = "api_ctf"

    __table_args__ = (
        UniqueConstraint("id", "range_uuid", name="idx_id_range_uuid"),
        UniqueConstraint("name", "range_uuid", name="idx_name_range_uuid"),
    )

    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    name = db.Column(db.String(120), unique=True)
    start_date = db.Column(db.Integer)  # 开始时间
    end_date = db.Column(db.Integer)  # 结束时间
    describe = db.Column(db.TEXT)
    user_id = db.Column(db.Integer, db.ForeignKey(User.id))
    level = db.Column(db.Integer)  # 关卡数
    type = db.Column(db.Integer, index=True, default=0)  # 类型，默认0 非场景类，1场景类
    range_uuid = db.Column(
        db.String(40),
        db.ForeignKey(RangeLinkModule.range_uuid, ondelete="SET NULL"),
        unique=True,
    )
    linkQuestion = db.relationship("CtfLinkQuestion", backref="Ctf", uselist=True)
    userList = db.relationship("CtfLinkUser", backref="Ctf", uselist=True)
    status = db.Column(db.Integer, index=True, default=0)  # 0:未开启，1:进行中，2:已结束

    def to_dict(self):
        return {
            "id": self.id,
            "name": self.name,
            "level": self.level,
            "start_date": self.start_date,
            "end_date": self.end_date,
            "range_uuid": self.range_uuid,
            "linkQuestion": [_.to_dict() for _ in self.linkQuestion],
        }


# ctf 关联课题表
class CtfLinkQuestion(db.Model, BaseModel):
    __tablename__ = "api_ctf_link_question"
    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    ctf_id = db.Column(
        db.Integer, db.ForeignKey(Ctf.id, ondelete="CASCADE"), index=True
    )  # 关联具体ctf项目id
    level_number = db.Column(db.Integer)  # 第几关
    ctf_question_id = db.Column(db.Integer, db.ForeignKey(CtfQuestion.id))  # 关联问题id
    question = db.relationship("CtfQuestion", uselist=True, backref="CtfLinkQuestion")
    # submit_question_info =  db.relationship("CtfUserFlag", uselist=True, backref='CtfLinkQuestion')

    def to_dict(self):
        return {
            "id": self.id,
            "ctf_link_question_id": self.id,
            "ctf_id": self.ctf_id,
            "number": self.level_number,
            "questionInfo": [_.to_dict() for _ in self.question],
            # 'submitQuestion': [_.to_dict() for _ in self.submit_question_info]
        }


# ctf 关卡
class CtfLevel(db.Model, BaseModel):
    __tablename__ = "api_ctf_level"
    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    ctf_id = db.Column(
        db.Integer, db.ForeignKey(Ctf.id, ondelete="CASCADE"), index=True, unique=True
    )  # 关联具体ctf项目id
    level_number_score = db.Column(db.String(200))  # 总分数 {"1": 11232, "2": 192}


# ctf 关联课题表
class CtfLinkUser(db.Model, BaseModel):
    __tablename__ = "api_ctf_user"
    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    ctf_id = db.Column(
        db.Integer, db.ForeignKey(Ctf.id, ondelete="CASCADE"), index=True
    )  # 关联具体ctf项目id
    ctfInfo = db.relationship("Ctf", uselist=False, backref="CtfLinkUser")
    user_id = db.Column(db.Integer, db.ForeignKey(User.id))
    questions = db.Column(db.Integer, default=0)  # 答题数
    totalScore = db.Column(db.Integer, default=0)  # 总分数
    userinfo = db.relationship("User", uselist=False, backref="CtfLinkUser")


# ctf 用户答题表
class CtfUserFlag(db.Model, BaseModel):
    __tablename__ = "api_ctf_user_flag"
    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    ctf_id = db.Column(
        db.Integer, db.ForeignKey(Ctf.id, ondelete="CASCADE"), index=True
    )  # 关联具体ctf项目id
    ctf_link_question_id = db.Column(
        db.Integer, db.ForeignKey(CtfLinkQuestion.id, ondelete="CASCADE"), index=True
    )  # 关联具体ctf项目id
    user_id = db.Column(db.Integer, db.ForeignKey(User.id))
    level_number = db.Column(db.Integer)  # 第几关
    submit_flag = db.Column(db.String(120))
    score = db.Column(db.Integer, default=None)
    userinfo = db.relationship("User", uselist=False, backref="CtfUserFlag")
    ctfLinkQuestion = db.relationship(
        "CtfLinkQuestion", uselist=False, backref="CtfUserFlag"
    )

    def to_dict(self):
        return {
            "id": self.id,
            "submit_flag": self.submit_flag,
            "ctf_id": self.ctf_id,
        }

class CtfUserFlagAuditLog(db.Model, BaseModel):
    __tablename__ = "api_ctf_user_flag_audit_log"
    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    ctf_id = db.Column(
        db.Integer, db.ForeignKey(Ctf.id, ondelete="CASCADE"), index=True
    )  # 关联具体ctf项目id
    checkpoint = db.Column(db.String(250),default=None)
    stu_name = db.Column(db.String(20),default=None)
    log_str = db.Column(db.String(20),default=None)
    question_name = db.Column(db.String(200),default=None)
    question_flag = db.Column(db.String(250),default=None)
    audit_score = db.Column(db.Integer, default=None)

    def to_dict(self):
        return {
            "id": self.id,
            "ctf_id": self.ctf_id,
            "audit_log": self.audit_log,
            "audit_score": self.audit_score
        }


####################################
# 红蓝比赛Model
####################################


class GroupType(Enum):
    red = "red"
    blue = "blue"


class CtfRedBlue(db.Model, BaseModel):
    """红蓝对抗比赛信息"""

    __tablename__ = "api_ctf_red_blue"
    __table_args__ = (
        UniqueConstraint("id", "range_uuid", name="idx_id_range_uuid"),
        UniqueConstraint("name", "range_uuid", name="idx_name_range_uuid"),
    )

    class StatusChoices(IntegerChoices):
        wait_start = 0, "待开始"
        in_progress = 1, "进行中"
        closed = 2, "已关闭"

    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    name = db.Column(db.String(120), unique=True)
    start_date = db.Column(db.Integer, comment="比赛开始时间")
    end_date = db.Column(db.Integer, comment="比赛结束时间")
    describe = db.Column(db.TEXT, comment="比赛描述")
    status = db.Column(
        db.Integer,
        default=0,
        nullable=False,
        comment="比赛状态，0:未开启，1:进行中，2:已结束",
    )

    red_initial_score = db.Column(db.Integer, default=0, comment="红队初始分")
    blue_initial_score = db.Column(db.Integer, default=100, comment="蓝队初始分")

    king = db.Column(
        db.Enum(*(val.value for val in GroupType)),
        nullable=True,
        comment="胜方，red：攻击方，blue：防守方",
    )

    # 关联靶场信息
    # range_uuid = db.Column(db.String(40), index=True, unique=True, comment="关联靶场ID")
    range_uuid = db.Column(
        db.String(40),
        db.ForeignKey(RangeLinkModule.range_uuid, ondelete="SET NULL"),
        unique=True,
    )

    # 关联攻守组，一对多(二)，红蓝
    groups = db.relationship(
        "CtfRedBlueGroup",
        uselist=True,
        backref=db.backref("ctf_red_blue", cascade="delete"),
    )
    # 关联成绩信息（一对多）
    link_score = db.relationship(
        "LinkCtfRedBlueScoreInfo",
        uselist=True,
        foreign_keys="LinkCtfRedBlueScoreInfo.ctf_id",
        backref=db.backref("ctf_red_blue"),
        cascade="all, delete-orphan",
    )
    # 关联预设flag列表 一对多
    flag_list = db.relationship(
        "CtfRedBlueFlag",
        uselist=True,
        backref=db.backref("ctf_red_blue"),
        cascade="all, delete-orphan",
    )

    rb_pilot_id = db.Column(db.Integer, nullable=False, comment="绑定导调用户ID")
    rb_referee_id = db.Column(db.Integer, nullable=False, comment="绑定裁判用户ID")

    create_user_id = db.Column(db.Integer, nullable=False, comment="创建用户ID")

    # 审计详情备份，日后需要再增加
    # audit_detail = db.relationship('CtfRedBlueAudit', uselist=True, backref=db.backref('ctf_red_blue', lazy='dynamic'), cascade='all, delete-orphan')  # noqa

    def to_dict(self):
        return {
            "id": self.id,
            "name": self.name,
            "start_date": self.start_date,
            "end_date": self.end_date,
            "describe": self.describe,
            "status": self.status,
            "range_uuid": self.range_uuid,
            "red_initial_score": self.red_initial_score,
            "blue_initial_score": self.blue_initial_score,
            "groups": self.groups,
            "king": self.king,
        }

    @property
    def rb_pilot(self):
        return User.query.get(self.rb_pilot_id)

    @property
    def rb_referee(self):
        return User.query.get(self.rb_referee_id)

class CtfScore(db.Model, BaseModel):
    """
    记录每隔一小时分数
    """
    __tablename__ = "api_ctf_score"
    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    ctf_red_blue_id = db.Column(db.Integer)
    sort_id = db.Column(db.Integer)
    score_info = db.Column(db.JSON, default="")

class CtfNotice(db.Model, BaseModel):
    """
    红蓝对抗-公告表
    """
    __tablename__ = "api_ctf_notice"
    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    title = db.Column(db.String(50), comment="公告标题")
    content = db.Column(db.String(500), comment="公告内容")
    level = db.Column(db.Integer, default=0, comment="公告等级  0:不紧急 1:紧急")
    visual_range = db.Column(db.Integer, default=0, comment="可见范围 0：全部人员 其他情况传入比赛id")
    notice_status_info = db.Column(db.JSON, comment="比赛关联学生公告查询信息", default="")


class CtfRedBlueGroup(db.Model, BaseModel):
    __tablename__ = "api_ctf_red_blue_group"
    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    group_type = db.Column(
        db.Enum(*(val.value for val in GroupType)),
        default="red",
        nullable=False,
        comment="攻防方，red：攻击方，blue：防守方",
    )
    ctf_red_blue_id = db.Column(
        db.Integer, db.ForeignKey(CtfRedBlue.id), comment="关联红蓝比赛ID"
    )
    # 关联成绩相关信息 一对多
    link_score = db.relationship(
        "LinkCtfRedBlueScoreInfo",
        uselist=True,
        foreign_keys="LinkCtfRedBlueScoreInfo.group_id",
        backref=db.backref("ctf_red_blue_group"),
    )


class LinkCtfRedBlueScoreInfo(db.Model, BaseModel):

    __tablename__ = "api_ctf_red_blue_link_score_info"

    id = db.Column(db.Integer, primary_key=True, autoincrement=True)

    ctf_id = db.Column(
        db.Integer, db.ForeignKey(CtfRedBlue.id), index=True, comment="红蓝比赛ID"
    )
    user_id = db.Column(db.Integer, db.ForeignKey(User.id), index=True, comment="用户ID")
    group_id = db.Column(
        db.Integer, db.ForeignKey(CtfRedBlueGroup.id), index=True, comment="组ID"
    )

    # 关联人工审计信息 一对一
    manual_audit_info = db.relationship(
        "CtfRedBlueManualAudit",
        uselist=False,
        backref=db.backref("link_score_info"),
        cascade="all, delete-orphan",
    )
    # 关联成绩记录 一对一 红方得分会自动生成一条蓝方扣分记录
    score_info = db.relationship(
        "CtfRedBlueScore",
        uselist=False,
        backref=db.backref("link_score_info"),
        cascade="all, delete-orphan",
    )
    # 关联flag记录 一对一
    flag_info = db.relationship(
        "CtfRedBlueFlag", uselist=False, backref=db.backref("link_score_info")
    )


class CtfRedBlueManualAudit(db.Model, BaseModel):
    """人工裁决记录表"""

    __tablename__ = "api_ctf_red_blue_manual_audit"

    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    title = db.Column(db.String(100), comment="人工裁决标题")
    apply_describe = db.Column(db.TEXT, comment="申请裁决时描述")
    file_name = db.Column(db.String(100), comment="申请裁决时附件名")

    referee_id = db.Column(db.Integer, comment="裁判ID")
    audit_describe = db.Column(db.TEXT, comment="裁判裁决原因")
    is_success = db.Column(db.Boolean, nullable=False, default=0, comment="裁决是否成功")

    score = db.Column(db.Integer, default=0, nullable=False, comment="裁决成功得分")
    is_processed = db.Column(db.Boolean, default=0, nullable=False, comment="是否已处理")
    link_info_id = db.Column(
        db.Integer,
        db.ForeignKey(LinkCtfRedBlueScoreInfo.id),
        index=True,
        comment="关联成绩相关信息ID",
    )


class CtfRedBlueScore(db.Model, BaseModel):
    """红蓝对抗裁决得分表
    分数变化存在1-2条记录
    攻击方得分同时防守方减分
    防守方人工判定单独得分
    """

    __tablename__ = "api_ctf_red_blue_score"

    class ChangeTypeEnum(Enum):
        plus = "得分"
        less = "扣分"

    class RecordTypeEnum(Enum):
        auto = "自动裁决"
        manual = "人工裁决"
        link = "关联裁决"  # 攻击方得分防守方关联扣分
        admin = "管理员操作"

    id = db.Column(db.Integer, primary_key=True, autoincrement=True)

    score = db.Column(db.Integer, comment="分数变化")
    change_type = db.Column(
        db.Enum(*(val.name for val in ChangeTypeEnum)),
        default=ChangeTypeEnum.plus.name,
        nullable=False,
        comment="分数变化类型，plus: 得分, less：扣分",
    )
    record_type = db.Column(
        db.Enum(*(val.name for val in RecordTypeEnum)),
        default=RecordTypeEnum.auto.name,
        nullable=False,
        comment="记录类型，auto: 自动审核，manual: 人工审核, link: 关联得/扣分, admin: 管理员操作",
    )
    link_info_id = db.Column(
        db.Integer,
        db.ForeignKey(LinkCtfRedBlueScoreInfo.id),
        index=True,
        comment="关联成绩相关信息ID",
    )


class CtfRedBlueFlag(db.Model, BaseModel):
    """红蓝对抗答案预设
    红方用户提交蓝方flag，则红得分
    """

    class ModelType(Enum):
        web = "web"
        wincc = "wincc"
        PLC = "PLC"

    __tablename__ = "api_ctf_red_blue_flag"

    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    ctf_red_blue_id = db.Column(
        db.Integer, db.ForeignKey(CtfRedBlue.id), index=True, comment="关联红蓝比赛ID"
    )
    link_info_id = db.Column(
        db.Integer,
        db.ForeignKey(LinkCtfRedBlueScoreInfo.id),
        index=True,
        comment="关联成绩相关信息ID",
    )

    range_id = db.Column(db.String(120), comment="靶场模板ID")
    flag = db.Column(db.String(100), comment="flag内容(允许为空，供裁判选择)")
    describe = db.Column(db.TEXT, comment="漏洞描述")
    score = db.Column(db.Integer, comment="分数")
    type = db.Column(
        db.Enum("red", "blue"), default="red", nullable=False, comment="哪一方的flag"
    )
    is_master = db.Column(db.Boolean, default=0, comment="是否是当前模块主要漏洞")
    is_used = db.Column(db.Boolean, default=0, comment="是否已经被解答")
    # 兼容
    mark = db.Column(db.String(40), default="", comment="当前flag覆盖漏洞层级标记")

    image_id = db.Column(db.String(120), comment="来源镜像ID")
    flag_uuid = db.Column(db.String(40), comment="唯一标识")
    ctf_question_type = db.Column(db.Integer)  # 关联问题id
    ctf_question_id = db.Column(db.Integer)  # 关联问题id

    def to_dict(self):
        return {
            "ctf_red_blue_id": self.ctf_red_blue_id,
            "link_info_id": self.link_info_id,
            "range_id": self.range_id,
            "flag": self.flag,
            "describe": self.describe,
            "score": self.score,
            "type": self.type,
            "image_id": self.image_id,
            "flag_uuid": self.flag_uuid,
            "is_master": self.is_master,
            "is_used": self.is_used,
        }


# class CtfRedBlueAuditRecord(db.Model, BaseModel):
#     """红蓝对抗审计详情备份信息表（日后优化）"""
#
#     __tablename__ = "api_ctf_red_blue_audit"
#
#     class AuditType(Enum):
#         wazuh = "wazuh"
#         suricata = "suricata"
#
#     id = db.Column(db.Integer, primary_key=True, autoincrement=True)
#     ctf_red_blue_id = db.Column(db.Integer, db.ForeignKey('api_ctf_red_blue.id'), comment="关联红蓝对抗比赛ID")
#     audit_type = db.Column(
#         db.Enum(*[val.value for val in AuditType]),
#         default="suricata",
#         nullable=False,
#         comment="审计服务类型，wazuh: 主机审计, suricata：流量审计"
#     )
#     # from sqlalchemy.dialects.mysql import JSON
#     # MariaDB 不支持json字段
#     audit_detail = db.Column(db.TEXT, comment="单条审计记录")
